package com.dong.repeatsubmit.configration;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.lettuce.core.ReadFrom;
import io.lettuce.core.TimeoutOptions;
import io.lettuce.core.cluster.ClusterClientOptions;
import io.lettuce.core.cluster.ClusterTopologyRefreshOptions;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisClusterConfiguration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisNode;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;

import java.io.IOException;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ThreadPoolExecutor;

@Component
@Slf4j
@Configuration
public class Configurations{

    @Autowired
    RedisProperties properties;

    /**
     * 增加单redis配置
     *
     * @param redisProperties
     * @return
     */
    @Bean
    public RedisConnectionFactory redisConnectionFactory(RedisProperties redisProperties) {
         return this.getCluster(redisProperties);
    }


    /**
     * 集群Redis的链接配置   TODO 待优化
     * @param redisProperties
     * @return
     */
    private RedisConnectionFactory getCluster(RedisProperties redisProperties) {
        List<String> nodes = redisProperties.getCluster().getNodes();
        if (nodes == null || nodes.isEmpty()) {
            log.error("Redis node is not configured");
            System.exit(0);
        }
        GenericObjectPoolConfig genericObjectPoolConfig = new GenericObjectPoolConfig();
        genericObjectPoolConfig.setMaxIdle(redisProperties.getLettuce().getPool().getMaxIdle());
        genericObjectPoolConfig.setMinIdle(redisProperties.getLettuce().getPool().getMinIdle());
        genericObjectPoolConfig.setMaxTotal(redisProperties.getLettuce().getPool().getMaxActive());
        genericObjectPoolConfig.setMaxWaitMillis(redisProperties.getLettuce().getPool().getMaxWait().toMillis());
        genericObjectPoolConfig.setTimeBetweenEvictionRunsMillis(3000L);    // TODO 改成配置
        List<RedisNode> listNodes = new ArrayList<>();
        for (String node : nodes) {
            String[] ipAndPort = node.split(":");
            RedisNode redisNode = new RedisNode(ipAndPort[0], Integer.parseInt(ipAndPort[1]));
            listNodes.add(redisNode);
        }
        RedisClusterConfiguration redisClusterConfiguration = new RedisClusterConfiguration();
        if (!StringUtils.isEmpty(redisProperties.getPassword())) {
            redisClusterConfiguration.setPassword(redisProperties.getPassword()); //pwd
        }
        redisClusterConfiguration.setClusterNodes(listNodes);
        Integer maxRedirects = redisProperties.getCluster().getMaxRedirects();
        redisClusterConfiguration.setMaxRedirects((maxRedirects == null) ? 3 : maxRedirects.intValue());
        ClusterTopologyRefreshOptions topologyRefreshOptions = ClusterTopologyRefreshOptions.builder()
                .enablePeriodicRefresh(Duration.ofSeconds(10L))     // TODO 改成配置
                .enableAllAdaptiveRefreshTriggers().build();
        ClusterClientOptions clusterClientOptions = ClusterClientOptions.builder()
                .timeoutOptions(TimeoutOptions.enabled(Duration.ofSeconds(10L)))        // TODO 改成配置
                .topologyRefreshOptions(topologyRefreshOptions).build();
        LettucePoolingClientConfiguration lettucePoolingClientConfiguration = LettucePoolingClientConfiguration.builder()
                .commandTimeout(Duration.ofSeconds(10L))        // TODO 改成配置
                .poolConfig(genericObjectPoolConfig)
                .readFrom(ReadFrom.REPLICA_PREFERRED).clientOptions(clusterClientOptions).build();
        return new LettuceConnectionFactory(redisClusterConfiguration, lettucePoolingClientConfiguration);
    }


    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<String, Object> template = new RedisTemplate();
        template.setConnectionFactory(redisConnectionFactory);

        Jackson2JsonRedisSerializer jacksonSerial = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jacksonSerial.setObjectMapper(om);
        template.setValueSerializer((RedisSerializer) jacksonSerial);
        template.setKeySerializer((RedisSerializer) new StringRedisSerializer());
        template.setHashKeySerializer((RedisSerializer) new StringRedisSerializer());
        template.setHashValueSerializer((RedisSerializer) jacksonSerial);
        template.afterPropertiesSet();

        return template;
    }

    @Bean
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }

    @Bean(destroyMethod = "shutdown")
    RedissonClient redissonClient() throws IOException {
        Config config = new Config();
        List<String> nodes = properties.getCluster().getNodes();
        List<String> newNodes = new ArrayList<>();
        nodes.forEach(n-> newNodes.add("redis://"+n));
        String[] strings = newNodes.toArray(new String[0]);
        config.useClusterServers()
                .addNodeAddress(strings);

        return Redisson.create(config);
    }

    @Bean(value = "TaskExecutorConfig",destroyMethod = "destroy")
    public ThreadPoolTaskExecutor  TaskExecutorConfig(){
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
// 设置核心线程数等于系统核数，一般等于CPU的核心数
        executor.setCorePoolSize(10);
// 设置最大线程数
        executor.setMaxPoolSize(10);
//配置队列大小
        executor.setQueueCapacity(10);
// 设置线程活跃时间（秒）
        executor.setKeepAliveSeconds(30);
// 线程满了之后由调用者所在的线程来执行
// 拒绝策略：CALLER_RUNS：不在新线程中执行任务，而是由调用者所在的线程来执行
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
// 设置默认线程名称
        executor.setThreadNamePrefix("executor_");
// 等待所有任务结束后再关闭线程池
        executor.setWaitForTasksToCompleteOnShutdown(true);
//执行初始化
        executor.initialize();
        return executor;
    }


}
